﻿using System;
using System.Linq;
using System.Text;
using AchSegítő.extensions;

namespace AchSegítő.File
{
    /// <summary>
    /// How many transactions and total amounts?
    /// </summary>
    public class BatchControl
    {
        #region Private

        internal Boolean HasDebits = false;
        internal Boolean HasCredits = false;

        #endregion

        /// <summary>
        /// THIS IS THE FIRST POSITION FOR ALL RECORD FORMATS. THE CODE IS
        /// UNIQUE FOR EACH RECORD TYPE. THE COMPANY/BATCH CONTROL RECORD
        /// USES RECORD TYPE CODE 8.
        /// </summary>
        public RecordTypeCodes RecordType { get; set; } = RecordTypeCodes.CompanyBatchControl;

        /// <summary>
        /// THE SERVICE CLASS CODE DEFINES THE TYPE OF 02-04 ENTRIES CONTAINED IN THE BATCH.
        /// </summary>
        public ServiceClassCode ServiceClass { get; set; } = ServiceClassCode.Mixed_Debits_And_Credits;

        public String CompanyId { get; set; } = String.Empty;

        /// <summary>
        /// COUNT IS A TALLY OF EACH TYPE ‘6’ RECORD AND IF USED, ALSO EACH ADDENDA WITHIN THE BATCH.
        /// </summary>
        public int EntryCount { get; set; } = 0;

        /// <summary>
        /// FOR EACH ORIGINATED TRANSACTION, YOU HAVE
        /// GENERATED A TYPE ‘6’ OR ENTRY DETAIL RECORD. ON THE
        /// ENTRY DETAIL RECORD THERE IS A RECEIVING DEPOSITORY
        /// FINANANCIAL INSTITUTION (RDFI)IDENTIFICATION(TRANSIT
        /// ROUTING NUMBER) LOCATED IN POSITIONS 4 THROUGH 11.
        /// THE FIRST 8 DIGITS OF EACH RDFI’s TRANSIT ROUTING
        /// NUMBER IS TREATED AS A NUMBER.

        /// ALL TRANSIT ROUTING NUMBERS WITHIN THE BATCH ARE
        /// ADDED TOGETHER FOR THE ENTRY HASH ON THE TYPE '8',
        /// BATCH CONTROL RECORD.ALL TRANSIT ROUTING NUMBERS
        /// WITHIN EACH FILE ARE ADDED TOGETHER TO CALCULATE THE
        /// VALUE OF THE ENTRY HASH ON THE TYPE '9', FILE CONTROL
        /// RECORD. (NOTE: DO NOT INCLUDE THE CHECK DIGIT OF THE
        /// TRANSIT ROUTING NUMBER, POSITION 12, IN THIS
        /// CALCULATION.) THE ENTRY HASH CALCULATIION CHECK IS
        /// USED IN THE PNC BANK FILE EDITING PROCESS TO HELP
        /// ENSURE DATA INTEGRITY OF THE BATCH AND FILE
        /// GENERATED BY YOUR PROCESSING.
        /// </summary>
        public String EntryHash { get; set; } = String.Empty;

        /// <summary>
        /// SUM TOTAL OF ALL DEBIT AMOUNTS WITHIN BATCH’S TYPE ‘6’ RECORD.
        /// </summary>
        public Decimal TotalDebits { get; set; } = decimal.Zero;

        /// <summary>
        /// SUM TOTAL OF ALL CREDIT AMOUNTS WITHIN BATCH’S TYPE ‘6’ RECORD
        /// </summary>
        public Decimal TotalCredits { get; set; } = decimal.Zero;

        /// <summary>
        /// TAX ID PREFIXED WITH A NUMERIC
        /// </summary>
        public String CompanyIdentification { get; set; } = String.Empty;

        public readonly String MessageAuthenticationCode = String.Empty; 
        public readonly String Reserved = "      ";

        /// <summary>
        /// FIRST 8 DIGITS OF BANK ABA NUMBER
        /// </summary>
        public String OriginatingDFIId { get; set; } = String.Empty;

        /// <summary>
        /// NUMBER ASSIGNED IN ASCENDING SEQUENCE TO EACH BATCH WITHIN THE FILE
        /// </summary>
        public int BatchNumber { get; set; } = 0;

        public BatchControl()
        { 
        }

        internal BatchControl(Batch batch)
        {
            this.ServiceClass = batch.Header.ServiceClass;
            this.CompanyId = batch.Header.CompanyId;
            this.BatchNumber = batch.Header.BatchNumber;

            this.EntryCount = batch.Entries.Count;

            this.EntryHash = CalculateEntryHash(batch);

            var debits = new[] { TransactionCode.CheckingDebit, TransactionCode.CheckingDebitPrenote, TransactionCode.SavingDebit, TransactionCode.SavingDebitPrenote };

            var credits = new[] { TransactionCode.CheckingCredit, TransactionCode.CheckingCreditPrenote, TransactionCode.SavingCredit, TransactionCode.SavingCreditPrenote };

            this.TotalDebits = batch.Entries.Where(entry => debits.Contains(entry.TransactionCode)).Sum(entry => entry.Amount);

            this.TotalCredits = batch.Entries.Where(entry => credits.Contains(entry.TransactionCode)).Sum(entry => entry.Amount);

            this.HasDebits = batch.Entries.Any(entry => debits.Contains(entry.TransactionCode));
            this.HasCredits = batch.Entries.Any(entry => credits.Contains(entry.TransactionCode));

            if (HasCredits && HasDebits)
            {
                this.ServiceClass = batch.Header.ServiceClass = ServiceClassCode.Mixed_Debits_And_Credits;
            }
            else if (HasDebits)
            {
                this.ServiceClass = batch.Header.ServiceClass = ServiceClassCode.Debits_Only;
            }
            else if (HasCredits)
            {
                this.ServiceClass = batch.Header.ServiceClass = ServiceClassCode.Credits_Only;
            }
        }

        public static String CalculateEntryHash(Batch batch)
        {
            var sum =
                batch
                .Entries
                .Select(entry => entry.ReceivingDFIIdentification)
                .Select(routingNumber => routingNumber.Substring(0, 8))
                .Select(substring => Decimal.Parse(substring))
                .Sum();

            //IF THE SUM OF THE RDFI TRANSIT ROUTING NUMBERS IS A
            //NUMBER GREATER THAN TEN DIGITS, REMOVE OR DROP THE
            //NUMBER OF DIGITS FROM THE LEFT SIDE OF THE NUMBER
            //UNTIL ONLY TEN DIGITS REMAIN. FOR EXAMPLE, IF THE SUM
            //OF THE TRANSIT ROUTING NUMBERS IS 234567898765,
            //REMOVE THE “23” FOR A HASH OF 4567898765.

            var last10 = sum % 10000000000;

            return last10.ToString();
        }

        public override string ToString()
        {
            var sb = new StringBuilder(ACH.CHARACTERS_PER_LINE);

            sb.Append((int)RecordType);

            sb.Append((int)ServiceClass);

            sb.Append(EntryCount.ToString().TrimAndPadLeft(6, '0'));

            sb.Append(EntryHash.TrimAndPadLeft(10, '0'));

            sb.Append((TotalDebits * 100).ToString("0").TrimAndPadLeft(12, '0'));

            sb.Append((TotalCredits * 100).ToString("0").TrimAndPadLeft(12, '0'));

            sb.Append(CompanyId.TrimAndPadRight(10));

            sb.Append(MessageAuthenticationCode.TrimAndPadRight(19));

            sb.Append(Reserved.TrimAndPadRight(6));

            sb.Append(OriginatingDFIId.TrimAndPadRight(8));

            sb.Append(BatchNumber.ToString().TrimAndPadLeft(7, '0'));

            sb.AppendLine();

            return sb.ToString();
        }
    }
}